import {Box2, Box3, Group, Object3D, Scene, Sprite, Vector3} from "three";
import {MapRenderer} from "./MapRenderer";
import {FreeMap} from "../";
import {Utils} from "../utils/Utils";


export class MapScene extends Scene {

    protected mapRenderer: MapRenderer;

    protected sprites: Array<Sprite> = [];

    constructor(mapRenderer: MapRenderer, map: FreeMap) {
        super();
        this.mapRenderer = mapRenderer;

        map.eventDispatcher.addEventListener('zoomchange', e => this.freshSprites(e));
        map.eventDispatcher.addEventListener('panend', e => this.freshSprites(e));
    }

    private freshSprites(e: any) {
        if (this.sprites.length != 0) {
            let camera = this.mapRenderer.camera;

            let resolution = camera.getResolution();

            let minPoint = new Vector3(camera.position.x - camera.width / 2, camera.position.y
                - camera.height * resolution / 2, 0);
            let maxPoint = new Vector3(camera.position.x + camera.width / 2, camera.position.y
                + camera.height * resolution / 2, 1);

            let screenBox = new Box3(minPoint, maxPoint);

            let showedSprite: Array<Sprite> = [];
            let notShowedSprite: Array<Sprite> = [];
            let screenSprite: Array<Sprite> = [];

            this.sprites.forEach(sprite => {
                sprite.scale.set(resolution * sprite.userData.rectWidth,
                    resolution * sprite.userData.rectHeight, 1);
                if (sprite.visible && screenBox.containsPoint(sprite.position)) {
                    showedSprite.push(sprite);
                    screenSprite.push(sprite);
                } else if (screenBox.containsPoint(sprite.position)) {
                    notShowedSprite.push(sprite);
                    screenSprite.push(sprite);
                }
            });

            if (e.type === 'zoomchange') {
                if (e.zoom > e.lastZoom) {
                    Utils.checkInHit(notShowedSprite, resolution, screenSprite);
                } else {
                    Utils.checkOutHit(showedSprite, resolution);
                }
            } else {
                console.log('checkHit');

                Utils.checkInHit(notShowedSprite, resolution, screenSprite);

                this.mapRenderer.render();
            }
        }
    }

    public addSpriteLayer(layerId: string, sprites: Array<Sprite>): Group {
        let group = this.addLayer(layerId, sprites);
        group.position.setZ(1);

        sprites.forEach(sprite => {
            this.sprites.push(sprite)
        });

        let r = this.mapRenderer.camera.getResolution();

        Utils.checkHit(this.sprites, r);

        this.mapRenderer.render();

        return group;
    }

    public addLayer(layerId: string, objects: Array<Object3D>): Group {
        this.removeLayer(layerId);

        let group = new Group();
        group.name = layerId;

        for (let mesh of objects) {
            group.add(mesh);
        }
        this.add(group);

        this.mapRenderer.render();

        return group;
    }

    public removeLayer(layerId: string) {
        let group = this.getObjectByName(layerId);

        if (group) {
            this.remove(group);
            this.mapRenderer.render();
        }
    }

    public addTile(layerId: string, mesh: Object3D, zoom: number) {
        let layerGroup = this.getObjectByName(layerId);

        if (layerGroup == undefined) {
            layerGroup = new Group();
            this.add(layerGroup);
        }

        let zoomGroup = layerGroup.getObjectByName(String(zoom));

        if (zoomGroup == undefined) {
            zoomGroup = new Group();
            layerGroup.add(zoomGroup);
        }

        let tileMesh = zoomGroup.getObjectByName(mesh.name);

        if (tileMesh == undefined) {
            zoomGroup.add(mesh);
        }

        this.mapRenderer.render();
    }

    removeTileZGroup(layerId: string) {
        let layerGroup = this.getObjectByName(layerId);

        if (layerGroup == undefined) {
            return
        }

        layerGroup.children.forEach(zoom => {
            layerGroup.remove(zoom);
        })
    }
}
